{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# City street network orientations\n",
    "\n",
    "Author: [Geoff Boeing](https://geoffboeing.com/)\n",
    "\n",
    "Compare the spatial orientations of city street networks with OSMnx.\n",
    "\n",
    "  - [Documentation](https://osmnx.readthedocs.io/)\n",
    "  - [Journal article and citation info](https://geoffboeing.com/publications/osmnx-paper/)\n",
    "  - [Code repository](https://github.com/gboeing/osmnx)\n",
    "  - [Examples gallery](https://github.com/gboeing/osmnx-examples)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-03-08T08:09:37.453744800Z",
     "start_time": "2025-03-08T08:09:33.776485400Z"
    }
   },
   "outputs": [
    {
     "data": {
      "text/plain": "'2.0.1'"
     },
     "execution_count": 1,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "import matplotlib.pyplot as plt\n",
    "import numpy as np\n",
    "import osmnx as ox\n",
    "\n",
    "weight_by_length = False\n",
    "\n",
    "ox.__version__"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-03-08T08:09:37.459745500Z",
     "start_time": "2025-03-08T08:09:37.455744700Z"
    }
   },
   "outputs": [],
   "source": [
    "# define the study sites as label : query\n",
    "places = {\n",
    "    # 'Atlanta'       : 'Atlanta, Georgia, USA',\n",
    "    # 'Boston'        : 'Boston, MA, USA',\n",
    "    \"Buffalo\": \"Buffalo, NY, USA\",\n",
    "    # 'Charlotte'     : 'Charlotte, NC, USA',\n",
    "    # 'Chicago'       : 'Chicago, IL, USA',\n",
    "    \"Cleveland\": \"Cleveland, OH, USA\",\n",
    "    # 'Dallas'        : 'Dallas, TX, USA',\n",
    "    # 'Houston'       : 'Houston, TX, USA',\n",
    "    # 'Denver'        : 'Denver, CO, USA',\n",
    "    # 'Detroit'       : 'Detroit, MI, USA',\n",
    "    # 'Las Vegas'     : 'Las Vegas, NV, USA',\n",
    "    # 'Los Angeles'   : {'city':'Los Angeles', 'state':'CA', 'country':'USA'},\n",
    "    # 'Manhattan'     : 'Manhattan, NYC, NY, USA',\n",
    "    \"Miami\": \"Miami, FL, USA\",\n",
    "    \"Minneapolis\": \"Minneapolis, MN, USA\",\n",
    "    # 'Orlando'       : 'Orlando, FL, USA',\n",
    "    # 'Philadelphia'  : 'Philadelphia, PA, USA',\n",
    "    # 'Phoenix'       : 'Phoenix, AZ, USA',\n",
    "    # 'Portland'      : 'Portland, OR, USA',\n",
    "    # 'Sacramento'    : 'Sacramento, CA, USA',\n",
    "    \"San Francisco\": {\"city\": \"San Francisco\", \"state\": \"CA\", \"country\": \"USA\"},\n",
    "    # 'Seattle'       : 'Seattle, WA, USA',\n",
    "    # 'St Louis'      : 'St. Louis, MO, USA',\n",
    "    # 'Tampa'         : 'Tampa, FL, USA',\n",
    "    \"Washington\": \"District of Columbia, USA\",\n",
    "}"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-03-08T08:09:53.529878Z",
     "start_time": "2025-03-08T08:09:37.460744200Z"
    }
   },
   "outputs": [
    {
     "data": {
      "text/plain": "                                            geometry   bbox_west  bbox_south  \\\n0  POLYGON ((-78.92235 42.94915, -78.91923 42.947...  -78.922353   42.826006   \n1  POLYGON ((-81.87909 41.39642, -81.87906 41.395...  -81.879094   41.390628   \n2  POLYGON ((-80.31976 25.76249, -80.31968 25.762...  -80.319760   25.709052   \n3  POLYGON ((-93.32912 44.92016, -93.32912 44.919...  -93.329125   44.890150   \n4  MULTIPOLYGON (((-123.17382 37.77573, -123.1737... -123.173825   37.640314   \n5  POLYGON ((-77.11979 38.93435, -77.11977 38.934...  -77.119795   38.791630   \n\n    bbox_east  bbox_north   place_id  osm_type   osm_id        lat  \\\n0  -78.795121   42.966449  322878240  relation   175031  42.886717   \n1  -81.532744   41.604436  340803346  relation   182130  41.499657   \n2  -80.139157   25.855783  395014896  relation  1216769  25.774173   \n3  -93.193859   45.051250  342813503  relation   136712  44.977300   \n4 -122.281479   37.929811  390482735  relation   111968  37.779259   \n5  -76.909366   38.995968  390895118  relation   162069  38.893847   \n\n          lon     class            type  place_rank  importance addresstype  \\\n0  -78.878392  boundary  administrative          14    0.670381        city   \n1  -81.693677  boundary  administrative          16    0.695233        city   \n2  -80.193620  boundary  administrative          16    0.725851        city   \n3  -93.265469  boundary  administrative          16    0.686917        city   \n4 -122.419329  boundary  administrative          12    0.771857        city   \n5  -76.988043  boundary  administrative           8    0.568156       state   \n\n                   name                                       display_name  \n0               Buffalo      Buffalo, Erie County, New York, United States  \n1             Cleveland    Cleveland, Cuyahoga County, Ohio, United States  \n2                 Miami   Miami, Miami-Dade County, Florida, United States  \n3           Minneapolis  Minneapolis, Hennepin County, Minnesota, Unite...  \n4         San Francisco           San Francisco, California, United States  \n5  District of Columbia                District of Columbia, United States  ",
      "text/html": "<div>\n<style scoped>\n    .dataframe tbody tr th:only-of-type {\n        vertical-align: middle;\n    }\n\n    .dataframe tbody tr th {\n        vertical-align: top;\n    }\n\n    .dataframe thead th {\n        text-align: right;\n    }\n</style>\n<table border=\"1\" class=\"dataframe\">\n  <thead>\n    <tr style=\"text-align: right;\">\n      <th></th>\n      <th>geometry</th>\n      <th>bbox_west</th>\n      <th>bbox_south</th>\n      <th>bbox_east</th>\n      <th>bbox_north</th>\n      <th>place_id</th>\n      <th>osm_type</th>\n      <th>osm_id</th>\n      <th>lat</th>\n      <th>lon</th>\n      <th>class</th>\n      <th>type</th>\n      <th>place_rank</th>\n      <th>importance</th>\n      <th>addresstype</th>\n      <th>name</th>\n      <th>display_name</th>\n    </tr>\n  </thead>\n  <tbody>\n    <tr>\n      <th>0</th>\n      <td>POLYGON ((-78.92235 42.94915, -78.91923 42.947...</td>\n      <td>-78.922353</td>\n      <td>42.826006</td>\n      <td>-78.795121</td>\n      <td>42.966449</td>\n      <td>322878240</td>\n      <td>relation</td>\n      <td>175031</td>\n      <td>42.886717</td>\n      <td>-78.878392</td>\n      <td>boundary</td>\n      <td>administrative</td>\n      <td>14</td>\n      <td>0.670381</td>\n      <td>city</td>\n      <td>Buffalo</td>\n      <td>Buffalo, Erie County, New York, United States</td>\n    </tr>\n    <tr>\n      <th>1</th>\n      <td>POLYGON ((-81.87909 41.39642, -81.87906 41.395...</td>\n      <td>-81.879094</td>\n      <td>41.390628</td>\n      <td>-81.532744</td>\n      <td>41.604436</td>\n      <td>340803346</td>\n      <td>relation</td>\n      <td>182130</td>\n      <td>41.499657</td>\n      <td>-81.693677</td>\n      <td>boundary</td>\n      <td>administrative</td>\n      <td>16</td>\n      <td>0.695233</td>\n      <td>city</td>\n      <td>Cleveland</td>\n      <td>Cleveland, Cuyahoga County, Ohio, United States</td>\n    </tr>\n    <tr>\n      <th>2</th>\n      <td>POLYGON ((-80.31976 25.76249, -80.31968 25.762...</td>\n      <td>-80.319760</td>\n      <td>25.709052</td>\n      <td>-80.139157</td>\n      <td>25.855783</td>\n      <td>395014896</td>\n      <td>relation</td>\n      <td>1216769</td>\n      <td>25.774173</td>\n      <td>-80.193620</td>\n      <td>boundary</td>\n      <td>administrative</td>\n      <td>16</td>\n      <td>0.725851</td>\n      <td>city</td>\n      <td>Miami</td>\n      <td>Miami, Miami-Dade County, Florida, United States</td>\n    </tr>\n    <tr>\n      <th>3</th>\n      <td>POLYGON ((-93.32912 44.92016, -93.32912 44.919...</td>\n      <td>-93.329125</td>\n      <td>44.890150</td>\n      <td>-93.193859</td>\n      <td>45.051250</td>\n      <td>342813503</td>\n      <td>relation</td>\n      <td>136712</td>\n      <td>44.977300</td>\n      <td>-93.265469</td>\n      <td>boundary</td>\n      <td>administrative</td>\n      <td>16</td>\n      <td>0.686917</td>\n      <td>city</td>\n      <td>Minneapolis</td>\n      <td>Minneapolis, Hennepin County, Minnesota, Unite...</td>\n    </tr>\n    <tr>\n      <th>4</th>\n      <td>MULTIPOLYGON (((-123.17382 37.77573, -123.1737...</td>\n      <td>-123.173825</td>\n      <td>37.640314</td>\n      <td>-122.281479</td>\n      <td>37.929811</td>\n      <td>390482735</td>\n      <td>relation</td>\n      <td>111968</td>\n      <td>37.779259</td>\n      <td>-122.419329</td>\n      <td>boundary</td>\n      <td>administrative</td>\n      <td>12</td>\n      <td>0.771857</td>\n      <td>city</td>\n      <td>San Francisco</td>\n      <td>San Francisco, California, United States</td>\n    </tr>\n    <tr>\n      <th>5</th>\n      <td>POLYGON ((-77.11979 38.93435, -77.11977 38.934...</td>\n      <td>-77.119795</td>\n      <td>38.791630</td>\n      <td>-76.909366</td>\n      <td>38.995968</td>\n      <td>390895118</td>\n      <td>relation</td>\n      <td>162069</td>\n      <td>38.893847</td>\n      <td>-76.988043</td>\n      <td>boundary</td>\n      <td>administrative</td>\n      <td>8</td>\n      <td>0.568156</td>\n      <td>state</td>\n      <td>District of Columbia</td>\n      <td>District of Columbia, United States</td>\n    </tr>\n  </tbody>\n</table>\n</div>"
     },
     "execution_count": 3,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# verify OSMnx geocodes each query to what you expect (i.e., a [multi]polygon geometry)\n",
    "gdf = ox.geocoder.geocode_to_gdf(list(places.values()))\n",
    "gdf"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-03-08T08:14:05.809667400Z",
     "start_time": "2025-03-08T08:09:53.529878Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "2025-03-08 16:09:53 Buffalo\n",
      "2025-03-08 16:10:13 Cleveland\n",
      "2025-03-08 16:10:55 Miami\n",
      "2025-03-08 16:11:45 Minneapolis\n",
      "2025-03-08 16:12:36 San Francisco\n",
      "2025-03-08 16:13:17 Washington\n"
     ]
    }
   ],
   "source": [
    "# create figure and axes\n",
    "n = len(places)\n",
    "ncols = int(np.ceil(np.sqrt(n)))\n",
    "nrows = int(np.ceil(n / ncols))\n",
    "figsize = (ncols * 5, nrows * 5)\n",
    "fig, axes = plt.subplots(nrows, ncols, figsize=figsize, subplot_kw={\"projection\": \"polar\"})\n",
    "\n",
    "# plot each city's polar histogram\n",
    "for ax, place in zip(axes.flat, sorted(places.keys())):\n",
    "    print(ox.utils.ts(), place)\n",
    "\n",
    "    # get undirected graphs with edge bearing attributes\n",
    "    G = ox.graph.graph_from_place(place, network_type=\"drive\")\n",
    "    Gu = ox.bearing.add_edge_bearings(ox.convert.to_undirected(G))\n",
    "    fig, ax = ox.plot.plot_orientation(Gu, ax=ax, title=place, area=True)\n",
    "\n",
    "# add figure title and save image\n",
    "suptitle_font = {\n",
    "    \"family\": \"DejaVu Sans\",\n",
    "    \"fontsize\": 60,\n",
    "    \"fontweight\": \"normal\",\n",
    "    \"y\": 1,\n",
    "}\n",
    "fig.suptitle(\"City Street Network Orientation\", **suptitle_font)\n",
    "fig.tight_layout()\n",
    "fig.subplots_adjust(hspace=0.35)\n",
    "fig.savefig(\"images/street-orientations.png\", facecolor=\"w\", dpi=100, bbox_inches=\"tight\")\n",
    "plt.close()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "You can also calculate the orientation entropy of a spatial graph with the `ox.bearing.orientation_entropy` function."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-03-08T08:14:05.814666800Z",
     "start_time": "2025-03-08T08:14:05.811662500Z"
    }
   },
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.12.8"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}
